#ifndef __IBASE_IMPL_H__
#define __IBASE_IMPL_H__

#include <memory>

#include "sync_obj.h"
#include "co_class_base.h"
#include "is_base_of.h"
#include "ref_obj_ptr.h"
#include "tools.h"


namespace Boss
{

  struct ISynObj
  {
    virtual ~ISynObj()
    {
    }
    virtual void Lock() = 0;
    virtual void UnLock() = 0;
    virtual std::auto_ptr<ISynObj> CreateNew() const = 0;
  };

  typedef SyncObj<ISynObj> ISyncObject;

  namespace Internal
  {

    template <typename T>
    class ISynObjImpl
      : private NonCopyable
      , public ISynObj
    {
    public:
      virtual void Lock()
      {
        SynObj.Lock();
      }
      virtual void UnLock()
      {
        SynObj.UnLock();
      }
      virtual std::auto_ptr<ISynObj> CreateNew() const
      {
        std::auto_ptr<ISynObj> Ret(new ISynObjImpl<T>);
        return Ret;
      }

    private:
      T SynObj;
    };

    class IBaseImplStub
      : private NonCopyable
      , public IBase
    {
    public:
      IBaseImplStub()
      {
      }
      virtual ~IBaseImplStub()
      {
      }
    };

    std::auto_ptr<ISynObj> CreateSynObject();

    template <typename T = TL::NullType>
    class ModuleCounter
    {
    public:
      static ModuleCounter& GetInst();
      void Inc()
      {
        ISyncObject Lock(*SynObj.get());
        ++GlobalCounter;
      }
      void Dec()
      {
        ISyncObject Lock(*SynObj.get());
        --GlobalCounter;
      }
      ULong GetCounter() const
      {
        ISyncObject Lock(*SynObj.get());
        return GlobalCounter;
      }

    private:
      mutable std::auto_ptr<ISynObj> SynObj;
      ULong GlobalCounter;
      ModuleCounter()
        : SynObj(CreateSynObject())
        , GlobalCounter(0)
      {
      }
    };

    template <typename T>
    ModuleCounter<T>& ModuleCounter<T>::GetInst()
    {
      static ModuleCounter Inst;
      return Inst;
    }

    template <typename T>
    inline bool IsEqualGUID(T uuid1, T uuid2);

    template <>
    inline bool IsEqualGUID(const char *uuid1, const char *uuid2)
    {
      while (*uuid1 && *uuid2 && *uuid1++ == *uuid2++);
      return !(*uuid1 || *uuid2);
    }

    template <typename T, bool isCoClass>
    struct QueryIFace
    {
      template <typename Y>
      static TPtr Query(TGuid ifaceId, Y *coClass)
      {
        return IsEqualGUID(ifaceId, BOSS_UUID_OF(T)) ?
          static_cast<T *>(coClass) : static_cast<T *>(0);
      }
    };

    template <typename TList>
    struct QueryIFaceFromInherited
    {
      template <typename T>
      static TPtr Query(TGuid ifaceId, T *coClass)
      {
        typedef typename TList::Head CurType;
        const bool IsCoClass = !!IsBaseOf<Internal::CoClassBase, CurType>::IsBase;
        TPtr Ret = QueryIFace<CurType, IsCoClass>::Query(ifaceId, coClass);
        if (Ret)
          return Ret;
        return QueryIFaceFromInherited<typename TList::Tail>::Query(ifaceId, coClass);
      }
    };

    template <>
    struct QueryIFaceFromInherited<TL::NullType>
    {
      template <typename T>
      static TPtr Query(TGuid, T *)
      {
        return 0;
      }
    };

    template <typename T>
    struct QueryIFace<T, true>
    {
      template <typename Y>
      static TPtr Query(TGuid ifaceId, Y *coClass)
      {
        typedef InheritBaseIFacesList<typename T::TBaseList> NewCoClass;
        typedef typename T::TBaseList NewBaseList;
        return QueryIFaceFromInherited<NewBaseList>::Query(ifaceId, static_cast<NewCoClass *>(coClass));
      }
    };

    template<typename T>
    struct HasFinalizeCreateFunction
    {
      struct Yes
      {
        char ch[1];
      };
      struct No
      {
        char ch[10];
      };
      template<typename U, bool (U::*)()>
      struct CheckFn
      {
        typedef Yes Result;
      };

      template<typename U>
      static typename CheckFn<U, &U::FinalizeCreate>::Result HasFn(U*);
      static No HasFn(...);

      enum { Has = sizeof(HasFn(static_cast<T *>(0))) == sizeof(Yes) };
    };

    template<typename T>
    struct HasBeforeDestroyFunction
    {
      struct Yes
      {
        char ch[1];
      };
      struct No
      {
        char ch[10];
      };
      template<typename U, void (U::*)()>
      struct CheckFn
      {
        typedef Yes Result;
      };

      template<typename U>
      static typename CheckFn<U, &U::BeforeDestroy>::Result HasFn(U*);
      static No HasFn(...);

      enum { Has = sizeof(HasFn(static_cast<T *>(0))) == sizeof(Yes) };
    };

    template <bool hasMtd>
    struct CreateDestroyExecuter
    {
      template <typename T>
      static bool FinalizeCreate(T *obj)
      {
        try
        {
          return obj->FinalizeCreate();
        }
        catch (...)
        {
        }
        return false;
      }
      template <typename T>
      static void BeforeDestroy(T *obj)
      {
        obj->BeforeDestroy();
      }
    };

    template <>
    struct CreateDestroyExecuter<false>
    {
      template <typename T>
      static bool FinalizeCreate(T *)
      {
        return true;
      }
      template <typename T>
      static void BeforeDestroy(T *)
      {
      }
    };

    template <typename T, bool isCoClass>
    struct CreateDestroyFuncImpl;

    template <typename T>
    struct CreateDestroyFuncIter
    {
      typedef typename T::Head Cur;
      typedef typename T::Tail Tail;
      static bool FinalizeCreate(void *obj)
      {
        return CreateDestroyFuncImpl<Cur, !!IsBaseOf<CoClassBase, Cur>::IsBase>::FinalizeCreate(reinterpret_cast<Cur *>(obj)) &&
          CreateDestroyExecuter<!!HasFinalizeCreateFunction<T>::Has>::FinalizeCreate(reinterpret_cast<Cur *>(obj))
          && CreateDestroyFuncIter<Tail>::FinalizeCreate(obj);
      }
      static void BeforeDestroy(void *obj)
      {
        CreateDestroyFuncIter<Tail>::BeforeDestroy(obj);
        CreateDestroyExecuter<!!HasFinalizeCreateFunction<T>::Has>::BeforeDestroy(reinterpret_cast<Cur *>(obj));
        CreateDestroyFuncImpl<Cur, !!IsBaseOf<CoClassBase, Cur>::IsBase>::BeforeDestroy(reinterpret_cast<Cur *>(obj));
      }
    };

    template <>
    struct CreateDestroyFuncIter<TL::NullType>
    {
      static bool FinalizeCreate(void *)
      {
        return true;
      }
      static void BeforeDestroy(void *)
      {
      }
    };

    template <typename T>
    void BeforeDestroy(T *obj);

    template <typename T, bool isCoClass>
    struct CreateDestroyFuncImpl
    {
      typedef typename T::TBaseList BaseList;
      typedef typename BaseList::Head Base;
      static bool FinalizeCreate(T *obj)
      {
        if (!CreateDestroyFuncIter<BaseList>::FinalizeCreate(static_cast<Base *>(obj)) ||
          !CreateDestroyExecuter<!!HasFinalizeCreateFunction<T>::Has>::FinalizeCreate(obj))
        {
          Internal::BeforeDestroy(static_cast<Base *>(obj));
          return false;
        }
        return true;
      }
      static void BeforeDestroy(T *obj)
      {
        CreateDestroyExecuter<!!HasBeforeDestroyFunction<T>::Has>::BeforeDestroy(obj);
        CreateDestroyFuncIter<BaseList>::BeforeDestroy(static_cast<Base *>(obj));
      }
    };

    template <typename T>
    struct CreateDestroyFuncImpl<T, false>
    {
      static bool FinalizeCreate(T *obj)
      {
        return true;
      }
      static void BeforeDestroy(T *obj)
      {
      }
    };

    template <typename T>
    bool FinalizeCreate(T *obj)
    {
      return CreateDestroyFuncImpl<T, !!IsBaseOf<CoClassBase, T>::IsBase>::FinalizeCreate(obj);
    }

    template <typename T>
    void BeforeDestroy(T *obj)
    {
      return CreateDestroyFuncImpl<T, !!IsBaseOf<CoClassBase, T>::IsBase>::BeforeDestroy(obj);
    }

  }

  template <typename T>
  class IBaseImpl
    : private Internal::IBaseImplStub
    , public T
  {
  public:
    typedef IBaseImpl<T> ThisType;
    typedef RefObjPtr<ThisType> ThisTypePtr;

    virtual ULong BOSS_CALL_TYPE AddRef()
    {
      ISyncObject Lock(GetSynObj());
      return InternalAddRef();
    }
    virtual ULong BOSS_CALL_TYPE Release()
    {
      return InternalRelease();
    }
    virtual TResult BOSS_CALL_TYPE QueryInterface(TGuid ifaceId, TPtr *iface)
    {
      ISyncObject Lock(GetSynObj());
      if (Internal::IsEqualGUID(ifaceId, BOSS_UUID_OF(IBase)))
        *iface = static_cast<Internal::IBaseImplStub *>(this);
      else
      {
        if ((*iface = Internal::QueryIFaceFromInherited<typename T::TBaseList>::Query(ifaceId, static_cast<T *>(this))) == 0)
          return rcNoInterface;
      }
      InternalAddRef();
      return rcOk;
    }

    static ThisTypePtr Create(ISynObj &synObj)
    {
      ISynObjPtr NewSynObj(synObj.CreateNew());
      return Create(NewSynObj);
    }

    template <typename TSyn>
    static ThisTypePtr Create()
    {
      ISynObjPtr NewSynObj(new Internal::ISynObjImpl<TSyn>);
      return Create(NewSynObj);
    }
    static ThisTypePtr Create()
    {
      ISynObjPtr NewSynObj(Internal::CreateSynObject());
      return Create(NewSynObj);
    }

  protected:
    virtual IBase* GetThisIBase()
    {
      return static_cast<IBaseImplStub *>(this);
    }

  private:
    typedef std::auto_ptr<ISynObj> ISynObjPtr;
    mutable ISynObjPtr SynObj;
    ULong Counter;
    bool IsSuccessfulCreated;

    static ThisTypePtr Create(ISynObjPtr synObj)
    {
      ThisTypePtr NewInst(new ThisType(synObj));
      if (!Internal::FinalizeCreate(static_cast<T *>(NewInst.Get())))
        return ThisTypePtr(0);
      NewInst->IsSuccessfulCreated = true;
      return NewInst;
    }

    IBaseImpl(ISynObjPtr synObj)
      : SynObj(synObj)
      , Counter(0)
      , IsSuccessfulCreated(false)
    {
    }
    virtual ~IBaseImpl()
    {
    }
    virtual ISynObj& GetSynObj() const
    {
      return *SynObj.get();
    }
    ULong InternalAddRef()
    {
      Internal::ModuleCounter<>::GetInst().Inc();
      return ++Counter;
    }
    ULong InternalRelease()
    {
      ULong NewCounter = 0;
      {
        ISyncObject Lock(GetSynObj());
        NewCounter = --Counter;
        if (!NewCounter)
        {
          if (IsSuccessfulCreated)
            Internal::BeforeDestroy(static_cast<T *>(this));
          IsSuccessfulCreated = false;
        }
      }
      if (!NewCounter)
        delete this;
      Internal::ModuleCounter<>::GetInst().Dec();
      return NewCounter;
    }
  };

}

#endif  // !__IBASE_IMPL_H__
